#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<cstdio>
#include<cstdlib>
#include<string>

using namespace std;
/*
为什么条件变量进行等待的时候需要互斥量
条件等待是线程同步的一种手段，如果一个线程，条件不满足，一直等下去
都不会满足，所以必须要有一个线程通过某一个操作改变共享变量
是原先不满足的条件变量得到满足，并且友好的通知等待在条件变量
上的线程。
条件变量不会无缘无故的得到满足，这里必然会涉及到共享数据的变化
所以一定要用互斥锁来保护，如果没有互斥锁就无法安全的获取和修改
共享数据
//=============================变成思想
我们设计代码的时候应该先上锁，然后再检测条件变量看看是否满足条件，如果
条件不满足的话，我们再进行解锁，不能先解锁，因为在解锁的一刹那
间条件可能就已经满足了，但是我们这个时候如果已经解锁的话，锁
可能已经被别的线程给抢走了，这个信号已经错过了
=====================条件变量的使用规范
等待条件的代码
pthread_mutex_init(&mutex);
pthread_mutex_lock(&mutex);
while(!flag)
{
    pthread_cond_wait(cond,mutex);
}
pthread_mmutex_unlock(&mutex);
//给条件发送信号
pthread_lock(&mutex);
设置条件为真
pthread_cond_signal(&mutex,nullptr);
//最后这里记得解锁
pthread_cond_unlock(&mutex);
*/

// pthread_cond_t cond;
// pthread_mutex_t mutex;

// void* thread1(void* args)
// {
//     while(true)
//     {
//         pthread_cond_wait(&cond,&mutex);
//         cout<<"running..."<<endl;
//         sleep(1);
//     }
// }
// void* thread2(void* args)
// {
//     while(true)
//     {
//         pthread_cond_signal(&cond);
//         sleep(1);
//     }
// }
// void* thread3(void* args)
// {
//     while(true)
//     {
//         pthread_cond_wait(&cond,&mutex);
//         cout<<(char*)args<<":"<<"running"<<endl;
//         sleep(1);
//     }
// }
// int main()
// {
//     pthread_cond_init(&cond,nullptr);
//     pthread_mutex_init(&mutex,nullptr);

//     pthread_t t1,t2,t3;
//     pthread_create(&t1,nullptr,thread1,nullptr);
//     pthread_create(&t2,nullptr,thread2,nullptr);
//     pthread_create(&t3,nullptr,thread3,(void*)"thread 3");
//     pthread_join(t1,nullptr);
//     pthread_join(t2,nullptr);
//     pthread_join(t3,nullptr);

//     pthread_cond_destroy(&cond);
//     pthread_mutex_destroy(&mutex);
//     return 0;
// }
/*
pthread_cond_broadcast(pthread_cond_t* cond)
pthread_cond_signal(pthread_cond_t* cond)
唤醒等待，这个时候根据条件变量通知相应的线程，所需要的资源
已经准备就绪了不需要再继续等待了
*/
// pthread_cond_t cond;
// pthread_mutex_t mutex;
// //一个线程进行通知，另一个进行等待通知
// void* thread1(void* args)
// {
//     while(true)
//     {
//         pthread_cond_wait(&cond,&mutex);
//         cout<<"running..."<<endl;
//         sleep(1);
//     }
// }

// void* thread2(void* args)
// {
//     while(true)
//     {
//         pthread_cond_signal(&cond);
//         sleep(1);
//     }
// }
// int main()
// {
//     pthread_t t1,t2;
//     //先对条件变量进行初始化
//     pthread_cond_init(&cond,nullptr);
//     //条件变量与锁创建完成以后的第一件是请就是先初始化
//     pthread_mutex_init(&mutex,nullptr);
//     pthread_create(&t1,nullptr,thread1,nullptr);
//     pthread_create(&t2,nullptr,thread2,nullptr);
//     //线程创建出来，必须进行等待用来回收资源
//     pthread_join(t1,nullptr);
//     pthread_join(t2,nullptr);
//     //最后记住要销毁
//     pthread_mutex_destroy(&mutex);
//     pthread_cond_destroy(&cond);
//     return 0;
// }
/*
======================线程安全===========================
线程安全：
多个线程并发同一段代码的时候，不会出现不同的结果，常见对全局变量
或者静态变量进行操作，并且在没有锁的情况下，会出现该问题
重入：
同一个函数被不同的执行流调用，当前一个执行流还没有被执行完
就有其他的执行流再次进入，这个过程成为重入，运行结果不会出现
任何不同，则该函数可以成为可重入函数，否则的话成为不可重入
函数，强调一点就是，被多个执行流进入函数的执行结果不会发生
变化，才能被称为是可重入函数，否则的话被称为是不可重入函数

常见的线程不安全的问题：
不保护共享变量的函数
函数状态随着调用，状态发生改变的函数
返回指针指向静态变量指针的函数
调用线程不安全的函数
============================拓展
这里补充一点：如果返回指针指向的是局部变量，那么这个指针往往
是危险的指针，因为局部变量被执行完以后内存就会被清空，但是指针、
的指向是不会变的，所以如果不想让内存被释放的话，就可以使用静态
变量，函数调用结束以后仍然可以使用这个变量。
===========静态变量与线程安全========================
静态变量位于方法区，被所有的对象所共享，一旦静态变量被修改
那么所有的线程均可见，故这个时候的线程是不安全的
============常见的线程安全的问题=================
每个线程对于全局变量和静态变量只有读取的权限而没有写入的权限
所以一般来说这些线程是安全的，类或者接口对于线程而言是原子操作、
============拓展
这里的原子操作是指在执行过程当中不会被线程的调度机制所打断，执行
一个线程的过程不会切入到其他的线程当中去，当然这是多线程的
操作，如果在单线程当中，只有汇编语句只有一条我们就可以认为
这个一个原子操作。
多个线程之间的切换不能存在接口的二义性，这样子下来这个线程
就是安全的

======================常见的不可重入的情况
调用了malloc和free函数，因为malloc时使用全局链表来进行
管理的，当可重入函数体内使用了静态变量这个时候就变得不可重入

只要函数是可重入的，那么线程就是安全的，一般情况下
只要不调用malloc或者new开辟出的全局空间，简单来说可重入函数
就是可以被随时中断的函数，当下一次调用召回上下文数据的时候
函数执行不会有任何的问题

=============================死锁
死锁是指在一组进程中各个进程均占有不可释放的资源，但是又再
相互向其他线程申请根本不可能释放的资源，从而造成了永久等待
的现象
死锁的四个必要条件：
互斥条件：一个资源每次只能被一个执行流使用
请求与保持条件：一个执行流因为申请资源而阻塞的时候，对已经
获得的资源保持不释放
不剥夺条件：一个执行流已经获得了资源在没有使用的情况下
不能强行剥夺，
循环等待条件：若干个执行流之间形成一种头尾相接的循环等待的情况
========================条件变量
当一个线程互斥地访问队列的时候，他可能发现在其他线程改变状态
之前，这个线程什么也做不了。
    就比如说当一个线程想要访问队列的时候，却发现队列为空
那么这个时候它只能不断地对队列进行检测，访问，非常地浪费时间
导致操作系统的效率会非常的低下，那么这个时候就需要使用到
条件变量，从而有效地避免饥饿，这就叫做同步
竞争条件：因为时序问题，而导致程序异常，我们称之为竞态条件
=================接口的认识
int pthread_cond_init(pthread_cond_t* cond,变量的属性(nullptr) )
条件变量的销毁：
//因为销毁的时候不能连同锁一起销毁，必须先把锁释放再进行销毁
int pthread_cond_destroy(pthread_cond_t* cond,pthread_mutex_t* mutex)
等待条件满足
int pthread_cond_wait(pthread_cond_t* cond,pthread_mutex_t *mutex)
同样的道理要在这个条件变量上等待，同样地需要先释放掉锁才能够
回收线程的资源
这些函数在成功的时候就会返回0，如果失败的话就会返回错误码
====================同步与互斥
同步：在保证数据安全的前提下，让线程能够按照某种特定的顺序
访问临界资源
*/
//====================售票系统的改进方法
// int tickets=100;
// pthread_mutex_t mutex;
// void* threadRoutine(void* args)
// {
//     while(true)
//     {
//         pthread_mutex_lock(&mutex);
//         if(tickets > 0)
//         {
//             sleep(1);
//             cout<<(char*)args<<"send:"<<tickets<<"ticekts "<<endl;
//             tickets--;
//             pthread_mutex_unlock(&mutex);
//         }
//         else
//         {
//             pthread_mutex_unlock(&mutex);
//             break;
//         }
//     }
//     return nullptr;
// }
// int main()
// {
//     pthread_t t1,t2,t3;
//     pthread_create(&t1,nullptr,threadRoutine,(void*)"thread 1");
//     pthread_create(&t2,nullptr,threadRoutine,(void*)"thread 2");
//     pthread_create(&t3,nullptr,threadRoutine,(void*)"thread 3");
//     //线程创建出来以后要记得进行资源回收
//     pthread_join(t1,nullptr);
//     pthread_join(t2,nullptr);
//     pthread_join(t3,nullptr);
//     //销毁锁
//     pthread_mutex_destroy(&mutex);
//     return 0;
// }